package com.tj.bo.account.server.services;

import com.alibaba.fastjson.JSON;
import com.tj.bo.account.client.AccountBalanceChangeService;
import com.tj.bo.account.client.enums.AccountStateEnum;
import com.tj.bo.account.client.enums.ExpenditureEnum;
import com.tj.bo.account.client.enums.IncomeEnum;
import com.tj.bo.account.client.vo.AccountExpenditureVO;
import com.tj.bo.account.client.vo.AccountFreezeVO;
import com.tj.bo.account.client.vo.AccountIncomeVO;
import com.tj.bo.account.client.vo.AccountUnFreezeVO;
import com.tj.bo.account.server.dao.AccountDao;
import com.tj.bo.account.server.dao.AccountFlowDao;
import com.tj.bo.account.server.entity.AccountEntity;
import com.tj.bo.account.server.entity.AccountFlowEntity;
import com.tj.bo.common.conf.enums.AmountTypeEnum;
import com.tj.bo.common.conf.util.SerialNumberUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;

/**
 * 余额变动服务
 * User: anonymous
 * Date: 2020/06/11
 * Time: 下午2:16
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AccountBalanceChangeServiceImpl implements AccountBalanceChangeService {
    private static final Logger logger = LoggerFactory.getLogger(AccountBalanceChangeServiceImpl.class);
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private AccountFlowDao accountFlowDao;

    @Override
    public boolean income(AccountIncomeVO accountIncomeVO) {
        //锁账户
        AccountEntity accountEntity = accountDao.selectForUpdate(accountIncomeVO.getUserCode());
        if (accountEntity == null || accountEntity.getStatus() == AccountStateEnum.INVALID.getCode()) {
            return false;
        }
        BigDecimal oldAvailableBalance = accountEntity.getAvailableBalance();
        BigDecimal oldGrants = accountEntity.getGrants();
        BigDecimal oldBalance = accountEntity.getBalance();
        // 加余额  |加 赠送金额
        BigDecimal newAvailableBalance = oldAvailableBalance;
        BigDecimal newGrants = oldGrants;
        BigDecimal newBalance = oldBalance;
        BigDecimal grantsAmount = BigDecimal.ZERO;
        BigDecimal incomeAmount = accountIncomeVO.getAmount();
        BigDecimal changeGrants = BigDecimal.ZERO;

        if (IncomeEnum.DEPOSIT == accountIncomeVO.getIncomeEnum()) {//充值
            newAvailableBalance = newAvailableBalance.add(accountIncomeVO.getAmount());
            newGrants = oldGrants.add(accountIncomeVO.getGrants());
            newBalance = newAvailableBalance.add(accountEntity.getFrozenAmount()).add(newGrants);
            changeGrants = accountIncomeVO.getGrants();
        }else if(IncomeEnum.BONUS == accountIncomeVO.getIncomeEnum()){//奖励金
            newGrants = oldGrants.add(accountIncomeVO.getAmount());
            newBalance = newAvailableBalance.add(accountEntity.getFrozenAmount()).add(newGrants);
            changeGrants = accountIncomeVO.getAmount();
        }else if(IncomeEnum.TRADE_PROFIT == accountIncomeVO.getIncomeEnum()){//订单盈利
            List<AccountFlowEntity> accountFlowEntities = accountFlowDao.getAccountFlowByOrderNoAndType(accountIncomeVO.getOrderNo(), "EXPENDITURE");
            for(AccountFlowEntity entity : accountFlowEntities){
                if(entity.getAmountType() == AmountTypeEnum.BONUS.getCode()){
                    grantsAmount = grantsAmount.add(entity.getAmount());
                }
            }
            if(grantsAmount.compareTo(BigDecimal.ZERO) > 0){
                incomeAmount = incomeAmount.subtract(grantsAmount);
                newGrants = oldGrants.add(grantsAmount);
                newAvailableBalance = newAvailableBalance.add(incomeAmount);
            }else{
                newAvailableBalance = newAvailableBalance.add(accountIncomeVO.getAmount());
                newGrants = oldGrants.add(accountIncomeVO.getGrants());
            }
            newBalance = newAvailableBalance.add(accountEntity.getFrozenAmount()).add(newGrants);
        }
        //保存新的余额信息
        accountEntity.setBalance(newBalance);
        accountEntity.setAvailableBalance(newAvailableBalance);
        accountEntity.setGrants(newGrants);
        accountDao.updateByPrimaryKey(accountEntity);
        if(grantsAmount.compareTo(BigDecimal.ZERO) > 0){
            saveAccountFlow(grantsAmount, accountIncomeVO.getOrderNo(), accountEntity, "INCOME", accountIncomeVO.getIncomeEnum().name(), AmountTypeEnum.BONUS.getCode());
        }else if(changeGrants.compareTo(BigDecimal.ZERO) > 0){
            saveAccountFlow(changeGrants, accountIncomeVO.getOrderNo(), accountEntity, "INCOME", accountIncomeVO.getIncomeEnum().name(), AmountTypeEnum.BONUS.getCode());
        }
        saveAccountFlow(incomeAmount, accountIncomeVO.getOrderNo(), accountEntity, "INCOME", accountIncomeVO.getIncomeEnum().name(), AmountTypeEnum.NON_BONUS.getCode());
        return true;
    }

    /**
     * 出款
     */
    @Override
    public boolean expenditure(AccountExpenditureVO accountExpenditureVO) {
        //锁账户
        AccountEntity accountEntity = accountDao.selectForUpdate(accountExpenditureVO.getUserCode());
        if (accountEntity == null || accountEntity.getStatus() == AccountStateEnum.INVALID.getCode()) {
            return false;
        }
        BigDecimal amount = accountExpenditureVO.getAmount();
        ExpenditureEnum expenditureEnum = accountExpenditureVO.getExpenditureEnum();

        BigDecimal oldBalance = accountEntity.getBalance();
        BigDecimal oldAvailableBalance = accountEntity.getAvailableBalance();
        BigDecimal oldFrozenAmount = accountEntity.getFrozenAmount();
        BigDecimal oldGrantAmount = accountEntity.getGrants();
        BigDecimal newAvailableBalance = oldAvailableBalance;
        BigDecimal newFrozenAmount = oldFrozenAmount;
        BigDecimal newBalance = oldBalance;
        BigDecimal newGrantAmount = oldGrantAmount;

        //减 有效余额 | 冻结金额
        if (ExpenditureEnum.TRADE_BUY == expenditureEnum) {
            newAvailableBalance = oldAvailableBalance.subtract(amount);
            newBalance = oldBalance.subtract(amount);
        } else if (ExpenditureEnum.WITHDRAW == expenditureEnum) {
            newFrozenAmount = oldFrozenAmount.subtract(amount);
        }
        //检查余额
        if (newBalance.compareTo(BigDecimal.ZERO) < 0 || newFrozenAmount.compareTo(BigDecimal.ZERO) < 0) {
            logger.info("expenditure error:accountExpenditureVO:{},accountEntity:{}", JSON.toJSONString(accountExpenditureVO), JSON.toJSONString(accountEntity));
            return false;
        }
        //可提现金额不足，余额充足时，分两部分扣款 availableBalance|grant
        if(newBalance.compareTo(BigDecimal.ZERO) >= 0 && newAvailableBalance.compareTo(BigDecimal.ZERO) < 0){
            BigDecimal deductionGrantAmount = newAvailableBalance.negate();//因newAvailableBalance为负数，即需要扣减的金额
            newGrantAmount = newGrantAmount.subtract(deductionGrantAmount);
            amount = amount.subtract(deductionGrantAmount);
            newAvailableBalance = BigDecimal.ZERO;
            saveAccountFlow(deductionGrantAmount, accountExpenditureVO.getOrderNo(), accountEntity, "EXPENDITURE", expenditureEnum.name(), AmountTypeEnum.BONUS.getCode());
        }
        if(newGrantAmount.compareTo(BigDecimal.ZERO) < 0){
            logger.info("expenditure error:newGrantAmount:{},accountEntity:{}", newGrantAmount, JSON.toJSONString(accountEntity));
            return false;
        }
        newBalance = newAvailableBalance.add(newFrozenAmount).add(newGrantAmount);
        //保存新的余额信息
        accountEntity.setBalance(newBalance);
        accountEntity.setAvailableBalance(newAvailableBalance);
        accountEntity.setFrozenAmount(newFrozenAmount);
        accountEntity.setGrants(newGrantAmount);
        accountDao.updateByPrimaryKey(accountEntity);
        //保存流水
        saveAccountFlow(amount, accountExpenditureVO.getOrderNo(), accountEntity, "EXPENDITURE", expenditureEnum.name(), AmountTypeEnum.NON_BONUS.getCode());
        return true;
    }

    /**
     * 冻结
     */
    @Override
    public boolean freeze(AccountFreezeVO accountFreezeVO) {
        //锁账户
        AccountEntity accountEntity = accountDao.selectForUpdate(accountFreezeVO.getUserCode());
        if (accountEntity == null || accountEntity.getStatus() == AccountStateEnum.INVALID.getCode()) {
            return false;
        }
        BigDecimal oldAvailableBalance = accountEntity.getAvailableBalance();
        BigDecimal oldFrozenAmount = accountEntity.getFrozenAmount();
        BigDecimal amount = accountFreezeVO.getAmount();
        BigDecimal newAvailableBalance = oldAvailableBalance.subtract(amount);
        BigDecimal newFrozenAmount = oldFrozenAmount.add(amount);
        //检查余额
        if (newAvailableBalance.compareTo(BigDecimal.ZERO) < 0) {
            logger.info("freeze error:accountFreezeVO:{},accountEntity:{}", JSON.toJSONString(accountFreezeVO), JSON.toJSONString(accountEntity));
            return false;
        }
        BigDecimal newBalance = newAvailableBalance.add(newFrozenAmount).add(accountEntity.getGrants());
        accountEntity.setBalance(newBalance);
        accountEntity.setAvailableBalance(newAvailableBalance);
        accountEntity.setFrozenAmount(newFrozenAmount);
        accountDao.updateByPrimaryKey(accountEntity);
        saveAccountFlow(amount, accountFreezeVO.getOrderNo(), accountEntity, "FREEZE", "FREEZE", AmountTypeEnum.NON_BONUS.getCode());
        return true;
    }

    /**
     * 解冻
     */
    @Override
    public boolean unfreeze(AccountUnFreezeVO accountUnFreezeVO) {
        //锁账户
        AccountEntity accountEntity = accountDao.selectForUpdate(accountUnFreezeVO.getUserCode());
        if (accountEntity == null || accountEntity.getStatus() == AccountStateEnum.INVALID.getCode()) {
            return false;
        }
        BigDecimal oldAvailableBalance = accountEntity.getAvailableBalance();
        BigDecimal oldFrozenAmount = accountEntity.getFrozenAmount();
        BigDecimal amount = accountUnFreezeVO.getAmount();
        BigDecimal newAvailableBalance = oldAvailableBalance.add(amount);
        BigDecimal newFrozenAmount = oldFrozenAmount.subtract(amount);
        //检查余额
        if (newFrozenAmount.compareTo(BigDecimal.ZERO) < 0) {
            logger.info("unfreeze error:accountUnFreezeVO:{},accountEntity:{}", JSON.toJSONString(accountUnFreezeVO), JSON.toJSONString(accountEntity));
            return false;
        }
        BigDecimal newBalance = newAvailableBalance.add(newFrozenAmount).add(accountEntity.getGrants());
        accountEntity.setBalance(newBalance);
        accountEntity.setAvailableBalance(newAvailableBalance);
        accountEntity.setFrozenAmount(newFrozenAmount);
        accountDao.updateByPrimaryKey(accountEntity);
        saveAccountFlow(amount, accountUnFreezeVO.getOrderNo(), accountEntity, "UNFREEZE", "UNFREEZE", AmountTypeEnum.NON_BONUS.getCode());
        return true;
    }

    /**
     * 保存流水
     */
    private void saveAccountFlow(BigDecimal amount, String orderNo, AccountEntity accountEntity, String type, String subType, Integer amountType) {
        String accountFlowNo = SerialNumberUtil.getBizOrderNumber("A", "F");
        AccountFlowEntity accountFlowEntity = new AccountFlowEntity();
        accountFlowEntity.setAccountFlowNo(accountFlowNo);
        accountFlowEntity.setOrderNo(orderNo);
        accountFlowEntity.setAccountNo(accountEntity.getAccountNo());
        accountFlowEntity.setUserCode(accountEntity.getUserCode());
        accountFlowEntity.setType(type);
        accountFlowEntity.setSubType(subType);
        accountFlowEntity.setAmount(amount);
        accountFlowEntity.setBalance(accountEntity.getBalance());
        accountFlowEntity.setAmountType(amountType);
        accountFlowDao.insert(accountFlowEntity);
    }
}
